/***************************************************************************
 *   Copyright (C) 2004 by cyril dupuit                                    *
 *   cyrildupuit@hotmail.com                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//*****************************************************************************
// Nom du module : Console.c
// Description : Gestion de l'affichage en mode texte.
//*****************************************************************************
 
// Historique :
// 21/01/05 : Creation du module avec les fonctions/procedures "static" au
// fichier console.c. Ce changement permet une plus grande lisibilite.

#include "Misc/ConsProc.h"
#include "Kernel/Types.h"
#include "Config.h"
#include "Lib/StdLib/string.h"
#include "Lib/StdLib/stdio.h"
#include "Kernel/Core.h"
#include "Kernel/Semaphor.h"
#include "Misc/Console.h"
#include "Arch/Pentium/Kernel/malloc.h"
#include "Arch/Arch.h"
#include "Kernel/Errors.h"
#include "Kernel/Process.h"
#include "Kernel/Thread.h"

#include "Lib/StdLib/stdarg.h"
#include "Lib/StdLib/math.h"
#include "Kernel/List.h"


//Pointeur sur la Ram video
static Buffer_t * pVideo = (Buffer_t *)SCREEN_MEMORY;

extern CList_t ConsoleList;//Liste des consoles

extern KernelHead_t Kernel;//Tete du noyau

//*****************************************************************************
// But de la procedure : Affecter une console
// Supprime le timer et affecte la nouvelle console
// Entree : Aucune
// Sortie : Aucune
//*****************************************************************************
void ConsoleAffect(void)
{
	if(SemGive(ConsoleList.SemChange) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}
}

//*****************************************************************************
// But de la tache : Se mettre en attente sur un semaphore afin d'effectuer un
// changement de console
// Entree : Aucune
// Sortie : Aucune
//*****************************************************************************
void TaskConsoleChanger(void)
{
	while(1)
	{
		if(SemTake(ConsoleList.SemChange, SEM_WAIT_FOREVER) != TRUE)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}

		TimerRemove(ConsoleList.TimerID);
		ConsoleList.TimerID = 0;

		if(ConsoleSetFirst((ConsoleID_t)ConsoleList.pNext) != TRUE)
		{//La prochaine console n'est pas valide, prendre la courante
			if(ConsoleSetFirst((ConsoleID_t)ConsoleList.pCurrent) != TRUE)
			{//Une erreur est survenue au moment de changement de console
				FATAL_ERROR_IF(TRUE);
			}
		}
	}
}

//*****************************************************************************
// But de la tache : Se mettre en attente sur un semaphore afin d'effectuer une
// selection de console
// Entree : Aucune
// Sortie : Aucune
//*****************************************************************************
void TaskConsoleSelector(void)
{
	while(1)
	{
		if(SemTake(ConsoleList.SemSelect, SEM_WAIT_FOREVER) != TRUE)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}
		if(ConsoleSelect() == ERROR)
		{//La prochaine console n'est pas valide, prendre la courante
			FATAL_ERROR_IF(TRUE);
		}
	}
}

//*****************************************************************************
// But de la fonction : Aider l'utilisateur a selectionner une console
// Entree : Aucune
// Sottie : Aucune
// Parametre retourne : FALSE si console non change, ERROR si pConsole errone,
//  TRUE sinon
//*****************************************************************************
Status_t ConsoleSelect(void)
{
	CNode_t * pConsole;

	if(ConsoleList.Count == 1) return FALSE;
	
	if(SemTake(ConsoleList.SemID, SEM_WAIT_FOREVER) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}
	
	if(ConsoleList.pNext == NULL)
	{//Definir la console courante comme prochaine console
		ConsoleList.pNext = ConsoleList.pCurrent;
	}
	
	pConsole = ConsoleList.pNext;
	
	if(pConsole == NULL)
	{//Aucune console
		if(SemGive(ConsoleList.SemID) == ERROR)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}
		return FALSE;
	}
	
	if(ConsoleList.TimerID != 0)
	{//Supprimer le timer peut etre a t'il expire. Ne pas tester la fonction TimerRemove
		TimerRemove(ConsoleList.TimerID);
		ConsoleList.TimerID = 0;
	}
	
	ConsoleList.TimerID = TimerCreate((pFunction_t)ConsoleAffect, CONSOLE_AFFECT_TICKS);
	if(ConsoleList.TimerID == 0)
	{//Memoire insuffisante
		if(SemGive(ConsoleList.SemID) == ERROR)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}
		SetLastError(INSUFFICIENT_MEMORY_AVAILABLE);
		return ERROR;
	}
	
	pConsole = pConsole->pNext;
	
	ConsoleList.pNext = pConsole;

	ConsoleHeader(ConsoleList.pNext, ((BLUE << 4) | RED));

	if(SemGive(ConsoleList.SemID) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}

	return TRUE;
}

//*****************************************************************************
// But de la tache : Raffraichir la console courante
// Entree : Aucune
// Sortie : Aucune
//*****************************************************************************
void TaskRefreshConsole(void)
{
	while(1)
	{
		Redraw();
		ThreadDelay(CONSOLE_REFRESH_TICKS);
	}
}

//*****************************************************************************
// But de la procedure : Tracer une ligne de X1 a X2 a l'ordonne Y
// Entrees :
//	x1 : Point de depart des abscisses
//	Y : Ordonnees
//	x2 : Point d'arrive
//	c : Couleur de la ligne
// Sortie : Aucune
//*****************************************************************************
void Line(CNode_t * pConsole, int x1, int y, int x2, int c)
{
	int I;
	Buffer_t * pBuffer = pConsole->pBuffer;
	
	if(x1 > x2) return;

	if(x1 < 0) x1 = 0;
	if(y < 0) y = 0;
	if(x2 > 79) x2 = 79;
	if(y > 24) y = 24;
	
	for(I = x1; I < (x2 + 1); I++)
	{
		pBuffer[((y * 80) + I)] = ' ' + (((char)c << 4) << 8);
	}
}

//*****************************************************************************
//But de la procedure : Dessiner un point a l'ecran de la console
// Entrees :
//	pConsole : Pointeur sur la console
//	X : Abscisse
//	Y : Coordonne
//	Color : Couleur du "pixel"
// Sortie : Aucune
//*****************************************************************************
void Pixel(CNode_t * pConsole,Coord_t X, Coord_t Y, Color_t Color)
{
	Buffer_t * pBuffer = pConsole->pBuffer;//Pointer sur le tampon memoire de la console
	
	if(X < 0) return;
	if(Y < 0) return;
	if(X > 79) return;
	if(Y > 24) return;

	pBuffer[(Y * MAX_X) + X] = ' ' + (((char)Color << 4) << 8);
}

//*****************************************************************************
// But de la procedure : vider le buffer memoire associe a la console
// Entree : 
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void Clear(CNode_t * pConsole)
{
	Coord_t I;
	unsigned long Val;
	unsigned long * pBuffer;
	
	pBuffer = (unsigned long *)pConsole->pBuffer;

	Val = ((' ' + ((char)pConsole->DefaultAttribute << 8)) << 16) + (' ' + ((char)pConsole->DefaultAttribute << 8));
	for(I = 0; I < (MAX_Y * MAX_X) / 2; I++)
	{//Effacer l'ecran et retablir les parametres par defaut
		pBuffer[I] = Val;
	}
	//RAZ des parametres de la console
	pConsole->Window.X = 0;
	pConsole->Window.Y = 0;
	pConsole->Cursor.X = 0;
	pConsole->Cursor.Y = 0;
	pConsole->LineBegin.X = 0;
	pConsole->LineBegin.Y = 0;
	pConsole->LineEnd.X = 0;
	pConsole->LineEnd.Y = 0;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la fonction : Effacer les caracteres se trouvant a gauche du curseur
// Entrees :
//	pConsole : Pointeur sur la console
// Parametre retourne : FALSE si aucun caractere ecrit, TRUE sinon
//*****************************************************************************
Status_t BackSpace(CNode_t * pConsole)
{
	Coord_t Value, LineSize, I;
	Coord_t  X1, Y1, X2, Y2;
	Buffer_t * p;

	X1 = pConsole->Cursor.X - pConsole->Window.X;
	Y1 = pConsole->Cursor.Y - pConsole->Window.Y;
	
	Value = pConsole->Cursor.X + (pConsole->Cursor.Y * MAX_X);
	
	if(Value == 0) return FALSE;//Le curseur est en debut de 1er ligne
	
	//Calculer le nombre de caractere ecrit
	LineSize = (pConsole->LineEnd.Y - pConsole->Cursor.Y) * MAX_X;
	LineSize += pConsole->LineEnd.X - pConsole->Cursor.X;

	p = &pConsole->pBuffer[Value];
	//Deplacer les caracteres
	for(I = 0; I < LineSize; I++) p[I - 1] = p[I];
	
	if(pConsole->Cursor.X > 0)
	{
		pConsole->Cursor.X--;
	}
	else if(pConsole->Cursor.Y > pConsole->LineBegin.Y)
	{//Decaler d'une ligne
		pConsole->Cursor.X = MAX_X - 1;
		pConsole->Cursor.Y--;
	}

	if(pConsole->LineEnd.X > 0)
	{
		pConsole->LineEnd.X--;
	}
	else if(pConsole->LineEnd.Y > pConsole->LineBegin.Y)
	{//Changer de ligne la fin de ligne
		pConsole->LineEnd.X = MAX_X - 1;
		pConsole->LineEnd.Y--;
	}
	//Remplacer le caractere supprime par un espace en fin de ligne
	Value = (pConsole->LineEnd.X + (pConsole->LineEnd.Y * MAX_X));
	pConsole->pBuffer[Value] = ' ' + ((char)pConsole->DefaultAttribute << 8);

	X2 = pConsole->Cursor.X - pConsole->Window.X;
	Y2 = pConsole->Cursor.Y - pConsole->Window.Y;
	
	WindowPosition(pConsole);//Repositionnement de la fenetre
   
	Set(pConsole->Status, REFRESH);
	
	return TRUE;
}

//*****************************************************************************
// But de la procedure : Annoncer la fin de la saisie d'une ligne
// Entrees :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void Return(CNode_t * pConsole)
{
	pConsole->Cursor.Y = pConsole->LineEnd.Y + 1;
	pConsole->Cursor.X = 0;
	//Decaler tous les caracteres d'une ligne vers le haut si fin d'ecran
	BufferOffset(pConsole);
	//Reinitialiser les debuts de lignes
	pConsole->LineBegin.X = pConsole->Cursor.X;
	pConsole->LineBegin.Y = pConsole->Cursor.Y;
	pConsole->LineEnd.X = pConsole->Cursor.X;
	pConsole->LineEnd.Y = pConsole->Cursor.Y;

	WindowPosition(pConsole);//Repositionnement de la fenetre
}

//*****************************************************************************
// But de la procedure : Ecrire un caractere ainsi que son attribut a un
// emplacement specifique dans le tampon memoire
// Entrees :
//	pConsole : Pointeur sur la console
// C : Caractere a ecrire
// FontColor : Couleur du caractere
// BackColor : Couleur du fond d'ecran
//*****************************************************************************
void Write(CNode_t * pConsole, const char C)
{
	Buffer_t * pBuffer;
	Coord_t	LineSize, I, X1, Y1, X2, Y2;
	Buffer_t Char;
	Buffer_t * p;
	
	Char = ((pConsole->CurrentAttribute << 8) + ((Buffer_t)C & 0x00FF));
	
	pBuffer = pConsole->pBuffer;//Pointer sur le tampon memoire de la console
   
	if(!IsSet(pConsole->Status, INSERT_MODE))
	{//Mode reecriture
		p = &pBuffer[pConsole->Cursor.Y * MAX_X];
		p[pConsole->Cursor.X] = Char;
		if(pConsole->Cursor.X == pConsole->LineEnd.X) pConsole->LineEnd.X++;
		X1 = pConsole->Cursor.X - pConsole->Window.X;
		Y1 = pConsole->Cursor.Y - pConsole->Window.Y;
		pConsole->Cursor.X++;
	}
	else
	{//Mode insertion de caractere
		//Copie des caracteres succedant le curseur
		LineSize = (pConsole->LineEnd.Y - pConsole->Cursor.Y) * MAX_X;
		LineSize += pConsole->LineEnd.X - pConsole->Cursor.X;
		p = &pBuffer[pConsole->Cursor.X + (pConsole->Cursor.Y * MAX_X)];
		//Decaler le caractere
		for(I = LineSize; I > 0; I--) p[I] = p[I-1];
		X1 = pConsole->Cursor.X - pConsole->Window.X;
		Y1 = pConsole->Cursor.Y - pConsole->Window.Y;
		//Copie du caractere saisi par l'utilisateur
		p = &pBuffer[pConsole->Cursor.Y * MAX_X];
		p[pConsole->Cursor.X] = Char;
		pConsole->Cursor.X++;
		pConsole->LineEnd.X++;
	}
	
	if(pConsole->Cursor.X > (MAX_X - 1))
	{
		pConsole->Cursor.X = 0;
		pConsole->Cursor.Y++;
	}

	if(pConsole->LineEnd.X > (MAX_X - 1))
	{//Si fin de ligne alors retourner en debut de ligne
		pConsole->LineEnd.X = 0;
		pConsole->LineEnd.Y++;
		//Decaler tous les caracteres d'une ligne vers le haut si fin d'ecran
		BufferOffset(pConsole);
	}
	WindowPosition(pConsole);//Repositionnement de la fenetre
	
	X2 = pConsole->LineEnd.X - pConsole->Window.X;
	Y2 = pConsole->LineEnd.Y - pConsole->Window.Y;
	
	if(Y2 > Y1) Set(pConsole->Status, REFRESH);

	RedrawRegion(pConsole, X1, Y1, X2 + 1, Y2 + 1);
}

//*****************************************************************************
// But de la procedure : Decaler le contenu du buffer de la console vers le
// haut d'une ligne
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
Status_t BufferOffset(CNode_t * pConsole)
{
	Coord_t I, Value;
	Buffer_t * pBuffer = pConsole->pBuffer;
	Buffer_t * p0 = &pBuffer[0];
	Buffer_t * p1 = &pBuffer[MAX_X];
	
	if((pConsole->LineEnd.Y < MAX_Y)&&(pConsole->Cursor.Y < MAX_Y)) return FALSE;

	//Si le debut de ligne est superieur a 1 alors
	if(pConsole->LineBegin.Y > 0) pConsole->LineBegin.Y--;

	pConsole->LineEnd.Y--;
	pConsole->Cursor.Y--;
	
	//Decaler l'affichage d'une ligne
	Value = (MAX_Y - 1) * MAX_X;
	for(I = 0; I < Value; I++) *p0++ = *p1++;
	
	//Remplir la derniere ligne
	p0 = &pBuffer[(MAX_Y - 1) * MAX_X];
	for(I = 0; I < MAX_X; I++)
	{//Parcourir les colonnes
		p0[I] = (Buffer_t)(' ' + ((char)pConsole->CurrentAttribute << 8));
	}

	Set(pConsole->Status, REFRESH);
	
	return TRUE;
}

//*****************************************************************************
// But de la procedure : Recadrer la fenetre
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void WindowPosition(CNode_t * pConsole)
{
	Status_t MoveWindow = FALSE;
	
	if(pConsole->Cursor.X > (pConsole->Window.X + ((78 * WINDOW_SIZE_X) / 79)))
	{//Positionnement de la fenetre par rapport au curseur
		pConsole->Window.X = pConsole->Cursor.X - ((78 * WINDOW_SIZE_X) / 79);
		MoveWindow = TRUE;
	}
	if(pConsole->Cursor.X < pConsole->Window.X)
	{//Positionnement de la fenetre par rapport au curseur
		pConsole->Window.X = 0;
		MoveWindow = TRUE;
	}

	if(pConsole->Cursor.Y > (pConsole->Window.Y + ((24 * WINDOW_SIZE_Y) / 25)))
	{//Positionnement de la fenetre par rapport au curseur
		pConsole->Window.Y = pConsole->Cursor.Y - ((24 * WINDOW_SIZE_Y) / 25);
		MoveWindow = TRUE;
	}
	if(pConsole->Cursor.Y < pConsole->Window.Y)
	{//Positionnement de la fenetre par rapport au curseur
		pConsole->Window.Y = 0;
		MoveWindow = TRUE;
	}
	
	if(MoveWindow == TRUE) Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Afficher une chaine de caracteres de longueur Number
// a un endroit de l'ecran
// Entrees :
//	P : Coordonnees du point de depart de l'affichage
//	pString : Pointeur sur la chaine de caracteres
//	Number : Nombre de caracteres a ecrire
//	Attribute : Couleur d'arriere plan et des caracteres
// Sortie : Aucune
//*****************************************************************************
void PrintText(Point_t P, char * pString, int Number, Color_t Attribute)
{
	int Size = 0;
	int I;
	Buffer_t * pVideoTemp = pVideo;
	Buffer_t Attrb = ((char)(Attribute & 0x0F) << 8);//Mise en place des couleurs

	pVideoTemp += P.X + (WINDOW_SIZE_X * P.Y);//Positionnement

	if(pString != NULL)
	{
		Size = strlen(pString);
		//Si la taille de la chaine est > au nombre de caracteres prevu
		if(Size > Number) Size = Number;
		for(I = 0; I < Size; I++)
		{//Ecriture de la description de la console
			*pVideoTemp &= 0xF000;
			*pVideoTemp = *pVideoTemp | ((Buffer_t)pString[I] | Attrb);
			pVideoTemp++;
		}
	}

	while(Size < Number)
	{
		*pVideoTemp &= 0xF000;
		*pVideoTemp = *pVideoTemp | ((Buffer_t)' ' | Attrb);
		pVideoTemp++;
		Size++;
	}
}

//*****************************************************************************
// But de la procedure : Raffraichir l'affichage
// Entree : Aucune
// Sortie : Aucune
//*****************************************************************************
void Redraw(void)
{
	CNode_t * pConsole;
	Coord_t X, Y;
	unsigned long Position;

	//Semaphore de protection de la liste des consoles
	if(SemTake(ConsoleList.SemID, SEM_WAIT_FOREVER) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}
	
	pConsole = ConsoleList.pCurrent;
	if((pConsole == NULL)||(pConsole->ConsoleID != (ConsoleID_t)pConsole)) 
	{
		if(SemGive(ConsoleList.SemID) == ERROR)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}
		return;
	}
	//Semaphore de protection de la console
	if(SemTake(pConsole->Sem, SEM_WAIT_FOREVER) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}

	if(!IsSet(pConsole->Status, (REFRESH + PARTIAL_REFRESH)))
	{
		if(SemGive(pConsole->Sem) == ERROR)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}
		//Semaphore cde protection de la liste des consoles
		if(SemGive(ConsoleList.SemID) == ERROR)
		{//Une erreur est survenue dans la gestion du semaphore
			FATAL_ERROR_IF(TRUE);
		}
		return;
	}
	
	if(pConsole->Window.Y > pConsole->WindowLimit.Y) pConsole->Window.Y = pConsole->WindowLimit.Y;
	if(pConsole->Window.X > pConsole->WindowLimit.X) pConsole->Window.X = pConsole->WindowLimit.X;
	
	VideoMemCopy(pConsole);

	X = pConsole->Cursor.X - pConsole->Window.X;
	Y = pConsole->Cursor.Y - pConsole->Window.Y;
	if((pConsole->Cursor.X >= pConsole->Window.X)&&(X < WINDOW_SIZE_X)
	&&(pConsole->Cursor.Y >= pConsole->Window.Y)&&(Y < WINDOW_SIZE_Y))
	{//Si le curseur est dans la fenetre de visualisation
		if(ConsoleList.pNext == NULL) 
		{//Si l'entete n'est pas affichee, activer le curseur
			Position = ( X + (Y * WINDOW_SIZE_X));
			EnableCursor;//Activer le curseur
			SetCursorPosition(Position);//Positionnement du curseur
		}
	}
	else
	{//Desactiver le curseur
		DisableCursor;
	}
	if(IsSet(pConsole->Status, INSERT_MODE))
	{//Changement de l'apparence du curseur
		EnableCursorIns;
	}
	else DisableCursorIns;
	
	//Semaphore de protection de la console
	if(SemGive(pConsole->Sem) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}
	//Semaphore cde protection de la liste des consoles
	if(SemGive(ConsoleList.SemID) == ERROR)
	{//Une erreur est survenue dans la gestion du semaphore
		FATAL_ERROR_IF(TRUE);
	}
}

//*****************************************************************************
// But de la fonction : Afficher le nom ainsi que la description d'une console
// Entree :
//	pConsole : Pointeur sur la console
//	X : Abscisses de depart de l'entete
//	Y : Ordonnes de depart de l'entete
//	FontColor : Couleur des caracteres
//	BackColor : Couleur du fond d'ecran
// Parametre retourne : FALSE si aucune entete, TRUE sinon
//*****************************************************************************
Status_t ConsoleHeader(CNode_t * pConsole, Color_t Attribute)
{
	Point_t P;
	const char Name[] = " Name : ";
	const char Description[] = " Description : ";
	char Tab[HEADER_WIDTH + 1];
	
	P = ConsoleList.HeaderStart;

	DisableCursor;//Desactiver le curseur

	//Dessiner une boite a l'ecran
	FillBox(ConsoleList.HeaderStart, ConsoleList.Header, Attribute >> 4);

	strcpy(Tab, Name);
	if(pConsole->pName != NULL)
	{
		strncat(Tab, pConsole->pName, (HEADER_WIDTH - sizeof(Name)));
	}
	else strncat(Tab, "NULL", (HEADER_WIDTH - sizeof(Name)));
	P.Y++;
	PrintText(P, Tab, HEADER_WIDTH, Attribute);

	strcpy(Tab, Description);
	if(pConsole->pDescription != NULL)
	{
		strncat(Tab, pConsole->pDescription, (HEADER_WIDTH - sizeof(Description)));
	}
	else strncat(Tab, "NULL", (HEADER_WIDTH - sizeof(Description)));
	P.Y++;
	PrintText(P, Tab, HEADER_WIDTH, Attribute);

	return TRUE;
}

//*****************************************************************************
// But de la procedure : Dessiner une boite a l'ecran
// Entree :
//	Start : Coordonnees de depart de la boite
//	Size : Dimensions de la boite
//	BackColor : Couleur de l'arriere plan
// Sortie : Aucune
//*****************************************************************************
void FillBox(Point_t Start, Point_t Size, Color_t BackColor)
{
	Coord_t I, J;
	Buffer_t Val;

	//Caractere espace + Couleur decale de 8 bits
	Val = ' ' + ((unsigned char)(BackColor << 4) << 8);
    
	for(J = Start.Y; J < (Start.Y + Size.Y); J++)
	{
		for(I = Start.X; I < (Start.X + Size.X); I++)
		{
			pVideo[(I + (J * WINDOW_SIZE_X))] = Val;
		}
	}
}

//*****************************************************************************
// But de la procedure : Deplacer vers le haut d'une ligne la visualisation de
// la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void MoveUp(CNode_t * pConsole)
{
	if(pConsole->Window.Y > 0) pConsole->Window.Y--;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer vers le bas d'une ligne la visualisation de
// la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void MoveDown(CNode_t * pConsole)
{
	if(pConsole->Cursor.Y > (pConsole->Window.Y))
	{
		pConsole->Window.Y++;
	}
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer vers la gauche la visualisation de la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void MoveLeft(CNode_t * pConsole)
{
	if(pConsole->Window.X > 0) pConsole->Window.X--;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer vers la droite la visualisation de la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void MoveRight(CNode_t * pConsole)
{
	pConsole->Window.X++;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer vers le haut d'une page la visualisation de
// la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void PageUp(CNode_t * pConsole)
{
	if(pConsole->Window.Y > (WINDOW_SIZE_Y / 2)) pConsole->Window.Y -= (WINDOW_SIZE_Y / 2);

	else pConsole->Window.Y = 0;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer vers le bas d'une page la visualisation de
// la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void PageDown(CNode_t * pConsole)
{
	if(pConsole->Cursor.Y > (pConsole->Window.Y + (WINDOW_SIZE_Y / 2)))
	{//Afficher la fin du texte
		pConsole->Window.Y += (WINDOW_SIZE_Y / 2);
	}
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer d'une page vers la gauche la visualisation de
// la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void PageLeft(CNode_t * pConsole)
{
	if(pConsole->Window.X > (WINDOW_SIZE_X / 2))
	{
		pConsole->Window.X -= (WINDOW_SIZE_X / 2);
	}
	else pConsole->Window.X = 0;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Deplacer d'une page vers la droite la visualisation de
// la console
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void PageRight(CNode_t * pConsole)
{
	pConsole->Window.X += (WINDOW_SIZE_X / 2);
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Aller en debut de page
// Entree : 
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void PageHome(CNode_t * pConsole)
{
	pConsole->Window.X = 0;

	pConsole->Window.Y = 0;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Aller en fin de page
// Entree : 
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void PageEnd(CNode_t * pConsole)
{
	if(pConsole->Cursor.X > (WINDOW_SIZE_X / 2))
	{
		pConsole->Window.X = pConsole->Cursor.X;
	}
	else pConsole->Window.X = 0;

	if(pConsole->Cursor.Y > (WINDOW_SIZE_Y / 2))
	{//Afficher la fin du texte
		pConsole->Window.Y = (pConsole->Cursor.Y - (WINDOW_SIZE_Y / 2));
	}
	else pConsole->Window.Y = 0;
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Decaler le curseur a gauche
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void CursorLeft(CNode_t * pConsole)
{
	if((pConsole->Cursor.Y == pConsole->LineBegin.Y)&&(pConsole->Cursor.X == pConsole->LineBegin.X))
	{
		return;
	}

	if(pConsole->Cursor.X > 0) pConsole->Cursor.X--;
	else if(pConsole->Cursor.Y > pConsole->LineBegin.Y)
	{
		pConsole->Cursor.X = MAX_X - 1;
		pConsole->Cursor.Y--;
	}

	WindowPosition(pConsole);//Repositionnement de la fenetre
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Decaler le curseur a droite
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void CursorRight(CNode_t * pConsole)
{
	if((pConsole->Cursor.Y == pConsole->LineEnd.Y)&&(pConsole->Cursor.X == pConsole->LineEnd.X))
	{
		return;
	}

	if(pConsole->Cursor.X < (MAX_X - 1)) pConsole->Cursor.X++;
	else if(pConsole->Cursor.Y < pConsole->LineEnd.Y)
	{
		pConsole->Cursor.X = 0;
		pConsole->Cursor.Y++;
	}

	WindowPosition(pConsole);//Repositionnement de la fenetre
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Mettre ou non en mode insertion de caracteres
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void InsertMode(CNode_t * pConsole)
{
	if(!IsSet(pConsole->Status, INSERT_MODE))
	{
		Set(pConsole->Status, INSERT_MODE);
	}
	else Reset(pConsole->Status, INSERT_MODE);
}

//*****************************************************************************
// But de la fonction : Supprimer un caractere
// Entree :
//	pConsole : Pointeur sur la console
// Parametre retourne : TRUE si caractere supprime, FALSE sinon
//*****************************************************************************
Status_t Delete(CNode_t * pConsole)
{
	Coord_t Value, LineSize, I;
	Buffer_t * pBuffer;

	//Calculer le nombre de caractere ecrit
	LineSize = (pConsole->LineEnd.Y - pConsole->Cursor.Y) * MAX_X;
	LineSize += pConsole->LineEnd.X - pConsole->Cursor.X;

	if(LineSize == 0) return FALSE;

	pBuffer = pConsole->pBuffer;

	for(I = 0; I < LineSize; I++)
	{
		Value = pConsole->Cursor.X + (pConsole->Cursor.Y * MAX_X);
		pBuffer[(I + Value)] = pBuffer[(I + 1 + Value)];
	}

	if(pConsole->LineEnd.X > 0) 
	{
		pConsole->LineEnd.X--;
	}
	else if(pConsole->LineEnd.Y > pConsole->LineBegin.Y)
	{//Changer de ligne la fin de ligne
		pConsole->LineEnd.X = MAX_X - 1;
		pConsole->LineEnd.Y--;
	}
	//Remplacer le caractere supprime par un espace en fin de ligne
	Value = (pConsole->LineEnd.X + (pConsole->LineEnd.Y * MAX_X));
	pBuffer[Value] = ' ' + ((char)pConsole->DefaultAttribute << 8);
	
	WindowPosition(pConsole);//Repositionnement de la fenetre
   
	Set(pConsole->Status, REFRESH);
	
	return TRUE;
}

//*****************************************************************************
// But de la procedure : Aller en debut de ligne
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void Home(CNode_t * pConsole)
{
	pConsole->Cursor.X = pConsole->LineBegin.X;
	pConsole->Cursor.Y = pConsole->LineBegin.Y;

	WindowPosition(pConsole);//Repositionnement de la fenetre
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la procedure : Aller en fin de ligne
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void End(CNode_t * pConsole)
{
	pConsole->Cursor.X = pConsole->LineEnd.X;
	pConsole->Cursor.Y = pConsole->LineEnd.Y;

	WindowPosition(pConsole);//Repositionnement de la fenetre
	
	Set(pConsole->Status, REFRESH);
}

//*****************************************************************************
// But de la fonction : Placer le curseur a un endroit definit
// Entrees :
//	pConsole : Pointeur sur la console
//	pString : pointeur sur la chaine de caracteres contenant les coordonnees
//*****************************************************************************
Status_t SetCurPosition(CNode_t * pConsole, const char * pString)
{
	Coord_t Y;
	Coord_t X;

	if((*pString >= '0')&&(*pString <= '9'))
	{//Recuperation des dizaines
		Y = (*pString - '0') * 10;
		pString++;
		if((*pString >= '0')&&(*pString <= '9'))
		{//Recuperation des unites
			Y += *pString - '0';
			if(Y >= MAX_Y)	Y = MAX_Y - 1;
			pString++;
			if(*pString == ';')
			{
				pString++;
				if((*pString >= '0')&&(*pString <= '9'))
				{//Recuperation des dizaines
					X = (*pString - '0') * 10;
					pString++;
					if((*pString >= '0')&&(*pString <= '9'))
					{//Recuperation des dizaines
						X += *pString - '0';
						if(X > MAX_X) X = MAX_X;
						pString++;
						if(*pString == 'H')
						{
							pConsole->Cursor.X = X;
							pConsole->Cursor.Y = Y;
							pConsole->LineBegin.X = X;
							pConsole->LineBegin.Y = Y;
							pConsole->LineEnd.X = X;
							pConsole->LineEnd.Y = Y;
							
							Set(pConsole->Status, REFRESH);
							
							return TRUE;
						}
					}
	  			}
	  		}
	  	}
  	}

   return FALSE;
}

//*****************************************************************************
// But de la fonction : Changer les couleurs de caracteres et de fond d'ecran
// Entrees :
//	pConsole : Pointeur sur la console
//	pString : pointeur sur la chaine de caracteres contenant les couleurs
//*****************************************************************************
Status_t SetConsoleColor(CNode_t * pConsole, const char * pString)
{
	Color_t FontColor;
	Color_t BackColor;

	if((*pString >= '0')&&(*pString <= '9'))
	{//Recuperation des dizaines
		FontColor = (*pString - '0') * 10;
		pString++;
		if((*pString >= '0')&&(*pString <= '9'))
		{//Recuperation des unites
			FontColor += (*pString - '0');
			pString++;
			if(*pString == ';')
			{
				pString++;
				if((*pString >= '0')&&(*pString <= '9'))
				{//Recuperation des dizaines
					BackColor = (*pString - '0') * 10;
					pString++;
					if((*pString >= '0')&&(*pString <= '9'))
					{//Recuperation des dizaines
						BackColor += *pString - '0';
						pString++;
						if(*pString == 'C')
						{
							FontColor = (FontColor % MAX_COLOR);
							BackColor = (BackColor % MAX_COLOR);
							pConsole->CurrentAttribute = (char)((BackColor << 4) | FontColor);
							
							Set(pConsole->Status, REFRESH);
							
							return TRUE;
						}
					}
	  			}
	  		}
	  	}
  	}
   
   return FALSE;
}

//*****************************************************************************
// But de la procedure : Copier le contenu de la console courante en RAM video
// Entree :
//	pConsole : Pointeur sur la console
// Sortie : Aucune
//*****************************************************************************
void VideoMemCopy(CNode_t * pConsole)
{
	Coord_t X, Y, I = 0;
	Buffer_t * pBuf = pConsole->pBuffer;
	Buffer_t * pBuffer = &pBuf[(pConsole->Window.X + (pConsole->Window.Y * MAX_X))];
	
	if(ConsoleList.pNext == NULL)
	{//Entete non affiche
		if(IsSet(pConsole->Status, REFRESH))
		{//Retracer la console completement
			for(I = 0; I < (80 * 25); I++) pVideo[I] = pBuffer[I];
		}
		else if(IsSet(pConsole->Status, PARTIAL_REFRESH))
		{//Retracer une partie de la console
			if(pConsole->RedrawEnd.X > 80) pConsole->RedrawEnd.X = 80;
			if(pConsole->RedrawEnd.Y > 25) pConsole->RedrawEnd.Y = 25;
			for(Y = pConsole->RedrawStart.Y; Y < pConsole->RedrawEnd.Y; Y++)
			{//Recopier les ordonnees dans la RAM video
				for(X = pConsole->RedrawStart.X; X < pConsole->RedrawEnd.X; X++)
				{//Recopier les abscisses dans la RAM video
					I = X + (Y * MAX_X);
					pVideo[I] = pBuffer[I];//Ecriture du caractere et de l'attribut
				}
			}
		}
		//RAZ des variables de dessin
		Reset(pConsole->Status, REFRESH);
		Reset(pConsole->Status, PARTIAL_REFRESH);
		pConsole->RedrawStart.X = MAX_X;
		pConsole->RedrawStart.Y = MAX_Y;
		pConsole->RedrawEnd.X = 0;
		pConsole->RedrawEnd.Y = 0;
	}
	else
	{
		for(Y = 0; Y < 25; Y++)
		{//Recopier les ordonnees dans la RAM video
			for(X = 0; X < 80; X++)
			{//Recopier les abscisses dans la RAM video
				if(!((X >= ConsoleList.HeaderStart.X) \
				&&(X < (ConsoleList.HeaderStart.X + ConsoleList.Header.X))
				&&(Y >= ConsoleList.HeaderStart.Y) \
				&&(Y < (ConsoleList.HeaderStart.Y + ConsoleList.Header.Y))))
				{//X et Y sont en dehors de l'entete
					pVideo[I] = pBuffer[I];//Ecriture du caractere et de l'attribut
				}
				I++;
			}
		}
		//Afficher l'entete
		ConsoleHeader(ConsoleList.pNext, ((BLUE << 4) | RED));
	}
}

//*****************************************************************************
